1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 package com.sun.imageio.plugins.png;
27
28 import java.awt.Point;
29 import java.awt.Rectangle;
30 import java.awt.color.ColorSpace;
31 import java.awt.image.BufferedImage;
32 import java.awt.image.DataBuffer;
33 import java.awt.image.DataBufferByte;
34 import java.awt.image.DataBufferUShort;
35 import java.awt.image.Raster;
36 import java.awt.image.WritableRaster;
37 import java.io.BufferedInputStream;
38 import java.io.ByteArrayInputStream;
39 import java.io.DataInputStream;
40 import java.io.EOFException;
41 import java.io.InputStream;
42 import java.io.IOException;
43 import java.io.SequenceInputStream;
44 import java.util.ArrayList;
45 import java.util.Arrays;
46 import java.util.Enumeration;
47 import java.util.Iterator;
48 import java.util.zip.Inflater;
49 import java.util.zip.InflaterInputStream;
50 import javax.imageio.IIOException;
51 import javax.imageio.ImageReader;
52 import javax.imageio.ImageReadParam;
53 import javax.imageio.ImageTypeSpecifier;
54 import javax.imageio.metadata.IIOMetadata;
55 import javax.imageio.spi.ImageReaderSpi;
56 import javax.imageio.stream.ImageInputStream;
57 import com.sun.imageio.plugins.common.InputStreamAdapter;
58 import com.sun.imageio.plugins.common.ReaderUtil;
59 import com.sun.imageio.plugins.common.SubImageInputStream;
60 import java.io.ByteArrayOutputStream;
61 import sun.awt.image.ByteInterleavedRaster;
62
63 class PNGImageDataEnumeration implements Enumeration<InputStream> {
64
65 boolean firstTime = true;
66 ImageInputStream stream;
67 int length;
68
69 public PNGImageDataEnumeration(ImageInputStream stream)
70 throws IOException {
71 this.stream = stream;
72 this.length = stream.readInt();
73 int type = stream.readInt();
74 }
75
76 public InputStream nextElement() {
77 try {
78 firstTime = false;
79 ImageInputStream iis = new SubImageInputStream(stream, length);
80 return new InputStreamAdapter(iis);
81 } catch (IOException e) {
82 return null;
83 }
84 }
85
86 public boolean hasMoreElements() {
87 if (firstTime) {
88 return true;
89 }
90
91 try {
92 int crc = stream.readInt();
93 this.length = stream.readInt();
94 int type = stream.readInt();
95 if (type == PNGImageReader.IDAT_TYPE) {
96 return true;
97 } else {
98 return false;
99 }
100 } catch (IOException e) {
101 return false;
102 }
103 }
104 }
105
106 public class PNGImageReader extends ImageReader {
107
108
109
110
111
112
113
114
115
116 static final int IHDR_TYPE = 0x49484452;
117 static final int PLTE_TYPE = 0x504c5445;
118 static final int IDAT_TYPE = 0x49444154;
119 static final int IEND_TYPE = 0x49454e44;
120
121
122 static final int bKGD_TYPE = 0x624b4744;
123 static final int cHRM_TYPE = 0x6348524d;
124 static final int gAMA_TYPE = 0x67414d41;
125 static final int hIST_TYPE = 0x68495354;
126 static final int iCCP_TYPE = 0x69434350;
127 static final int iTXt_TYPE = 0x69545874;
128 static final int pHYs_TYPE = 0x70485973;
129 static final int sBIT_TYPE = 0x73424954;
130 static final int sPLT_TYPE = 0x73504c54;
131 static final int sRGB_TYPE = 0x73524742;
132 static final int tEXt_TYPE = 0x74455874;
133 static final int tIME_TYPE = 0x74494d45;
134 static final int tRNS_TYPE = 0x74524e53;
135 static final int zTXt_TYPE = 0x7a545874;
136
137 static final int PNG_COLOR_GRAY = 0;
138 static final int PNG_COLOR_RGB = 2;
139 static final int PNG_COLOR_PALETTE = 3;
140 static final int PNG_COLOR_GRAY_ALPHA = 4;
141 static final int PNG_COLOR_RGB_ALPHA = 6;
142
143
144 static final int[] inputBandsForColorType = {
145 1,
146 -1,
147 3,
148 1,
149 2,
150 -1,
151 4
152 };
153
154 static final int PNG_FILTER_NONE = 0;
155 static final int PNG_FILTER_SUB = 1;
156 static final int PNG_FILTER_UP = 2;
157 static final int PNG_FILTER_AVERAGE = 3;
158 static final int PNG_FILTER_PAETH = 4;
159
160 static final int[] adam7XOffset = { 0, 4, 0, 2, 0, 1, 0 };
161 static final int[] adam7YOffset = { 0, 0, 4, 0, 2, 0, 1 };
162 static final int[] adam7XSubsampling = { 8, 8, 4, 4, 2, 2, 1, 1 };
163 static final int[] adam7YSubsampling = { 8, 8, 8, 4, 4, 2, 2, 1 };
164
165 private static final boolean debug = true;
166
167 ImageInputStream stream = null;
168
169 boolean gotHeader = false;
170 boolean gotMetadata = false;
171
172 ImageReadParam lastParam = null;
173
174 long imageStartPosition = -1L;
175
176 Rectangle sourceRegion = null;
177 int sourceXSubsampling = -1;
178 int sourceYSubsampling = -1;
179 int sourceMinProgressivePass = 0;
180 int sourceMaxProgressivePass = 6;
181 int[] sourceBands = null;
182 int[] destinationBands = null;
183 Point destinationOffset = new Point(0, 0);
184
185 PNGMetadata metadata = new PNGMetadata();
186
187 DataInputStream pixelStream = null;
188
189 BufferedImage theImage = null;
190
191
192 int pixelsDone = 0;
193
194
195 int totalPixels;
196
197 public PNGImageReader(ImageReaderSpi originatingProvider) {
198 super(originatingProvider);
199 }
200
201 public void setInput(Object input,
202 boolean seekForwardOnly,
203 boolean ignoreMetadata) {
204 super.setInput(input, seekForwardOnly, ignoreMetadata);
205 this.stream = (ImageInputStream)input;
206
207
208 resetStreamSettings();
209 }
210
211 private String readNullTerminatedString(String charset, int maxLen) throws IOException {
212 ByteArrayOutputStream baos = new ByteArrayOutputStream();
213 int b;
214 int count = 0;
215 while ((maxLen > count++) && ((b = stream.read()) != 0)) {
216 if (b == -1) throw new EOFException();
217 baos.write(b);
218 }
219 return new String(baos.toByteArray(), charset);
220 }
221
222 private void readHeader() throws IIOException {
223 if (gotHeader) {
224 return;
225 }
226 if (stream == null) {
227 throw new IllegalStateException("Input source not set!");
228 }
229
230 try {
231 byte[] signature = new byte[8];
232 stream.readFully(signature);
233
234 if (signature[0] != (byte)137 ||
235 signature[1] != (byte)80 ||
236 signature[2] != (byte)78 ||
237 signature[3] != (byte)71 ||
238 signature[4] != (byte)13 ||
239 signature[5] != (byte)10 ||
240 signature[6] != (byte)26 ||
241 signature[7] != (byte)10) {
242 throw new IIOException("Bad PNG signature!");
243 }
244
245 int IHDR_length = stream.readInt();
246 if (IHDR_length != 13) {
247 throw new IIOException("Bad length for IHDR chunk!");
248 }
249 int IHDR_type = stream.readInt();
250 if (IHDR_type != IHDR_TYPE) {
251 throw new IIOException("Bad type for IHDR chunk!");
252 }
253
254 this.metadata = new PNGMetadata();
255
256 int width = stream.readInt();
257 int height = stream.readInt();
258
259
260 stream.readFully(signature, 0, 5);
261 int bitDepth = signature[0] & 0xff;
262 int colorType = signature[1] & 0xff;
263 int compressionMethod = signature[2] & 0xff;
264 int filterMethod = signature[3] & 0xff;
265 int interlaceMethod = signature[4] & 0xff;
266
267
268 stream.skipBytes(4);
269
270 stream.flushBefore(stream.getStreamPosition());
271
272 if (width == 0) {
273 throw new IIOException("Image width == 0!");
274 }
275 if (height == 0) {
276 throw new IIOException("Image height == 0!");
277 }
278 if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 &&
279 bitDepth != 8 && bitDepth != 16) {
280 throw new IIOException("Bit depth must be 1, 2, 4, 8, or 16!");
281 }
282 if (colorType != 0 && colorType != 2 && colorType != 3 &&
283 colorType != 4 && colorType != 6) {
284 throw new IIOException("Color type must be 0, 2, 3, 4, or 6!");
285 }
286 if (colorType == PNG_COLOR_PALETTE && bitDepth == 16) {
287 throw new IIOException("Bad color type/bit depth combination!");
288 }
289 if ((colorType == PNG_COLOR_RGB ||
290 colorType == PNG_COLOR_RGB_ALPHA ||
291 colorType == PNG_COLOR_GRAY_ALPHA) &&
292 (bitDepth != 8 && bitDepth != 16)) {
293 throw new IIOException("Bad color type/bit depth combination!");
294 }
295 if (compressionMethod != 0) {
296 throw new IIOException("Unknown compression method (not 0)!");
297 }
298 if (filterMethod != 0) {
299 throw new IIOException("Unknown filter method (not 0)!");
300 }
301 if (interlaceMethod != 0 && interlaceMethod != 1) {
302 throw new IIOException("Unknown interlace method (not 0 or 1)!");
303 }
304
305 metadata.IHDR_present = true;
306 metadata.IHDR_width = width;
307 metadata.IHDR_height = height;
308 metadata.IHDR_bitDepth = bitDepth;
309 metadata.IHDR_colorType = colorType;
310 metadata.IHDR_compressionMethod = compressionMethod;
311 metadata.IHDR_filterMethod = filterMethod;
312 metadata.IHDR_interlaceMethod = interlaceMethod;
313 gotHeader = true;
314 } catch (IOException e) {
315 throw new IIOException("I/O error reading PNG header!", e);
316 }
317 }
318
319 private void parse_PLTE_chunk(int chunkLength) throws IOException {
320 if (metadata.PLTE_present) {
321 processWarningOccurred(
322 "A PNG image may not contain more than one PLTE chunk.\n" +
323 "The chunk wil be ignored.");
324 return;
325 } else if (metadata.IHDR_colorType == PNG_COLOR_GRAY ||
326 metadata.IHDR_colorType == PNG_COLOR_GRAY_ALPHA) {
327 processWarningOccurred(
328 "A PNG gray or gray alpha image cannot have a PLTE chunk.\n" +
329 "The chunk wil be ignored.");
330 return;
331 }
332
333 byte[] palette = new byte[chunkLength];
334 stream.readFully(palette);
335
336 int numEntries = chunkLength/3;
337 if (metadata.IHDR_colorType == PNG_COLOR_PALETTE) {
338 int maxEntries = 1 << metadata.IHDR_bitDepth;
339 if (numEntries > maxEntries) {
340 processWarningOccurred(
341 "PLTE chunk contains too many entries for bit depth, ignoring extras.");
342 numEntries = maxEntries;
343 }
344 numEntries = Math.min(numEntries, maxEntries);
345 }
346
347
348 int paletteEntries;
349 if (numEntries > 16) {
350 paletteEntries = 256;
351 } else if (numEntries > 4) {
352 paletteEntries = 16;
353 } else if (numEntries > 2) {
354 paletteEntries = 4;
355 } else {
356 paletteEntries = 2;
357 }
358
359 metadata.PLTE_present = true;
360 metadata.PLTE_red = new byte[paletteEntries];
361 metadata.PLTE_green = new byte[paletteEntries];
362 metadata.PLTE_blue = new byte[paletteEntries];
363
364 int index = 0;
365 for (int i = 0; i < numEntries; i++) {
366 metadata.PLTE_red[i] = palette[index++];
367 metadata.PLTE_green[i] = palette[index++];
368 metadata.PLTE_blue[i] = palette[index++];
369 }
370 }
371
372 private void parse_bKGD_chunk() throws IOException {
373 if (metadata.IHDR_colorType == PNG_COLOR_PALETTE) {
374 metadata.bKGD_colorType = PNG_COLOR_PALETTE;
375 metadata.bKGD_index = stream.readUnsignedByte();
376 } else if (metadata.IHDR_colorType == PNG_COLOR_GRAY ||
377 metadata.IHDR_colorType == PNG_COLOR_GRAY_ALPHA) {
378 metadata.bKGD_colorType = PNG_COLOR_GRAY;
379 metadata.bKGD_gray = stream.readUnsignedShort();
380 } else {
381 metadata.bKGD_colorType = PNG_COLOR_RGB;
382 metadata.bKGD_red = stream.readUnsignedShort();
383 metadata.bKGD_green = stream.readUnsignedShort();
384 metadata.bKGD_blue = stream.readUnsignedShort();
385 }
386
387 metadata.bKGD_present = true;
388 }
389
390 private void parse_cHRM_chunk() throws IOException {
391 metadata.cHRM_whitePointX = stream.readInt();
392 metadata.cHRM_whitePointY = stream.readInt();
393 metadata.cHRM_redX = stream.readInt();
394 metadata.cHRM_redY = stream.readInt();
395 metadata.cHRM_greenX = stream.readInt();
396 metadata.cHRM_greenY = stream.readInt();
397 metadata.cHRM_blueX = stream.readInt();
398 metadata.cHRM_blueY = stream.readInt();
399
400 metadata.cHRM_present = true;
401 }
402
403 private void parse_gAMA_chunk() throws IOException {
404 int gamma = stream.readInt();
405 metadata.gAMA_gamma = gamma;
406
407 metadata.gAMA_present = true;
408 }
409
410 private void parse_hIST_chunk(int chunkLength) throws IOException,
411 IIOException
412 {
413 if (!metadata.PLTE_present) {
414 throw new IIOException("hIST chunk without prior PLTE chunk!");
415 }
416
417
418
419
420
421
422 metadata.hIST_histogram = new char[chunkLength/2];
423 stream.readFully(metadata.hIST_histogram,
424 0, metadata.hIST_histogram.length);
425
426 metadata.hIST_present = true;
427 }
428
429 private void parse_iCCP_chunk(int chunkLength) throws IOException {
430 String keyword = readNullTerminatedString("ISO-8859-1", 80);
431 metadata.iCCP_profileName = keyword;
432
433 metadata.iCCP_compressionMethod = stream.readUnsignedByte();
434
435 byte[] compressedProfile =
436 new byte[chunkLength - keyword.length() - 2];
437 stream.readFully(compressedProfile);
438 metadata.iCCP_compressedProfile = compressedProfile;
439
440 metadata.iCCP_present = true;
441 }
442
443 private void parse_iTXt_chunk(int chunkLength) throws IOException {
444 long chunkStart = stream.getStreamPosition();
445
446 String keyword = readNullTerminatedString("ISO-8859-1", 80);
447 metadata.iTXt_keyword.add(keyword);
448
449 int compressionFlag = stream.readUnsignedByte();
450 metadata.iTXt_compressionFlag.add(Boolean.valueOf(compressionFlag == 1));
451
452 int compressionMethod = stream.readUnsignedByte();
453 metadata.iTXt_compressionMethod.add(Integer.valueOf(compressionMethod));
454
455 String languageTag = readNullTerminatedString("UTF8", 80);
456 metadata.iTXt_languageTag.add(languageTag);
457
458 long pos = stream.getStreamPosition();
459 int maxLen = (int)(chunkStart + chunkLength - pos);
460 String translatedKeyword =
461 readNullTerminatedString("UTF8", maxLen);
462 metadata.iTXt_translatedKeyword.add(translatedKeyword);
463
464 String text;
465 pos = stream.getStreamPosition();
466 byte[] b = new byte[(int)(chunkStart + chunkLength - pos)];
467 stream.readFully(b);
468
469 if (compressionFlag == 1) {
470 text = new String(inflate(b), "UTF8");
471 } else {
472 text = new String(b, "UTF8");
473 }
474 metadata.iTXt_text.add(text);
475 }
476
477 private void parse_pHYs_chunk() throws IOException {
478 metadata.pHYs_pixelsPerUnitXAxis = stream.readInt();
479 metadata.pHYs_pixelsPerUnitYAxis = stream.readInt();
480 metadata.pHYs_unitSpecifier = stream.readUnsignedByte();
481
482 metadata.pHYs_present = true;
483 }
484
485 private void parse_sBIT_chunk() throws IOException {
486 int colorType = metadata.IHDR_colorType;
487 if (colorType == PNG_COLOR_GRAY ||
488 colorType == PNG_COLOR_GRAY_ALPHA) {
489 metadata.sBIT_grayBits = stream.readUnsignedByte();
490 } else if (colorType == PNG_COLOR_RGB ||
491 colorType == PNG_COLOR_PALETTE ||
492 colorType == PNG_COLOR_RGB_ALPHA) {
493 metadata.sBIT_redBits = stream.readUnsignedByte();
494 metadata.sBIT_greenBits = stream.readUnsignedByte();
495 metadata.sBIT_blueBits = stream.readUnsignedByte();
496 }
497
498 if (colorType == PNG_COLOR_GRAY_ALPHA ||
499 colorType == PNG_COLOR_RGB_ALPHA) {
500 metadata.sBIT_alphaBits = stream.readUnsignedByte();
501 }
502
503 metadata.sBIT_colorType = colorType;
504 metadata.sBIT_present = true;
505 }
506
507 private void parse_sPLT_chunk(int chunkLength)
508 throws IOException, IIOException {
509 metadata.sPLT_paletteName = readNullTerminatedString("ISO-8859-1", 80);
510 chunkLength -= metadata.sPLT_paletteName.length() + 1;
511
512 int sampleDepth = stream.readUnsignedByte();
513 metadata.sPLT_sampleDepth = sampleDepth;
514
515 int numEntries = chunkLength/(4*(sampleDepth/8) + 2);
516 metadata.sPLT_red = new int[numEntries];
517 metadata.sPLT_green = new int[numEntries];
518 metadata.sPLT_blue = new int[numEntries];
519 metadata.sPLT_alpha = new int[numEntries];
520 metadata.sPLT_frequency = new int[numEntries];
521
522 if (sampleDepth == 8) {
523 for (int i = 0; i < numEntries; i++) {
524 metadata.sPLT_red[i] = stream.readUnsignedByte();
525 metadata.sPLT_green[i] = stream.readUnsignedByte();
526 metadata.sPLT_blue[i] = stream.readUnsignedByte();
527 metadata.sPLT_alpha[i] = stream.readUnsignedByte();
528 metadata.sPLT_frequency[i] = stream.readUnsignedShort();
529 }
530 } else if (sampleDepth == 16) {
531 for (int i = 0; i < numEntries; i++) {
532 metadata.sPLT_red[i] = stream.readUnsignedShort();
533 metadata.sPLT_green[i] = stream.readUnsignedShort();
534 metadata.sPLT_blue[i] = stream.readUnsignedShort();
535 metadata.sPLT_alpha[i] = stream.readUnsignedShort();
536 metadata.sPLT_frequency[i] = stream.readUnsignedShort();
537 }
538 } else {
539 throw new IIOException("sPLT sample depth not 8 or 16!");
540 }
541
542 metadata.sPLT_present = true;
543 }
544
545 private void parse_sRGB_chunk() throws IOException {
546 metadata.sRGB_renderingIntent = stream.readUnsignedByte();
547
548 metadata.sRGB_present = true;
549 }
550
551 private void parse_tEXt_chunk(int chunkLength) throws IOException {
552 String keyword = readNullTerminatedString("ISO-8859-1", 80);
553 metadata.tEXt_keyword.add(keyword);
554
555 byte[] b = new byte[chunkLength - keyword.length() - 1];
556 stream.readFully(b);
557 metadata.tEXt_text.add(new String(b, "ISO-8859-1"));
558 }
559
560 private void parse_tIME_chunk() throws IOException {
561 metadata.tIME_year = stream.readUnsignedShort();
562 metadata.tIME_month = stream.readUnsignedByte();
563 metadata.tIME_day = stream.readUnsignedByte();
564 metadata.tIME_hour = stream.readUnsignedByte();
565 metadata.tIME_minute = stream.readUnsignedByte();
566 metadata.tIME_second = stream.readUnsignedByte();
567
568 metadata.tIME_present = true;
569 }
570
571 private void parse_tRNS_chunk(int chunkLength) throws IOException {
572 int colorType = metadata.IHDR_colorType;
573 if (colorType == PNG_COLOR_PALETTE) {
574 if (!metadata.PLTE_present) {
575 processWarningOccurred(
576 "tRNS chunk without prior PLTE chunk, ignoring it.");
577 return;
578 }
579
580
581 int maxEntries = metadata.PLTE_red.length;
582 int numEntries = chunkLength;
583 if (numEntries > maxEntries) {
584 processWarningOccurred(
585 "tRNS chunk has more entries than prior PLTE chunk, ignoring extras.");
586 numEntries = maxEntries;
587 }
588 metadata.tRNS_alpha = new byte[numEntries];
589 metadata.tRNS_colorType = PNG_COLOR_PALETTE;
590 stream.read(metadata.tRNS_alpha, 0, numEntries);
591 stream.skipBytes(chunkLength - numEntries);
592 } else if (colorType == PNG_COLOR_GRAY) {
593 if (chunkLength != 2) {
594 processWarningOccurred(
595 "tRNS chunk for gray image must have length 2, ignoring chunk.");
596 stream.skipBytes(chunkLength);
597 return;
598 }
599 metadata.tRNS_gray = stream.readUnsignedShort();
600 metadata.tRNS_colorType = PNG_COLOR_GRAY;
601 } else if (colorType == PNG_COLOR_RGB) {
602 if (chunkLength != 6) {
603 processWarningOccurred(
604 "tRNS chunk for RGB image must have length 6, ignoring chunk.");
605 stream.skipBytes(chunkLength);
606 return;
607 }
608 metadata.tRNS_red = stream.readUnsignedShort();
609 metadata.tRNS_green = stream.readUnsignedShort();
610 metadata.tRNS_blue = stream.readUnsignedShort();
611 metadata.tRNS_colorType = PNG_COLOR_RGB;
612 } else {
613 processWarningOccurred(
614 "Gray+Alpha and RGBS images may not have a tRNS chunk, ignoring it.");
615 return;
616 }
617
618 metadata.tRNS_present = true;
619 }
620
621 private static byte[] inflate(byte[] b) throws IOException {
622 InputStream bais = new ByteArrayInputStream(b);
623 InputStream iis = new InflaterInputStream(bais);
624 ByteArrayOutputStream baos = new ByteArrayOutputStream();
625
626 int c;
627 try {
628 while ((c = iis.read()) != -1) {
629 baos.write(c);
630 }
631 } finally {
632 iis.close();
633 }
634 return baos.toByteArray();
635 }
636
637 private void parse_zTXt_chunk(int chunkLength) throws IOException {
638 String keyword = readNullTerminatedString("ISO-8859-1", 80);
639 metadata.zTXt_keyword.add(keyword);
640
641 int method = stream.readUnsignedByte();
642 metadata.zTXt_compressionMethod.add(new Integer(method));
643
644 byte[] b = new byte[chunkLength - keyword.length() - 2];
645 stream.readFully(b);
646 metadata.zTXt_text.add(new String(inflate(b), "ISO-8859-1"));
647 }
648
649 private void readMetadata() throws IIOException {
650 if (gotMetadata) {
651 return;
652 }
653
654 readHeader();
655
656
657
658
659
660
661
662 int colorType = metadata.IHDR_colorType;
663 if (ignoreMetadata && colorType != PNG_COLOR_PALETTE) {
664 try {
665 while (true) {
666 int chunkLength = stream.readInt();
667 int chunkType = stream.readInt();
668
669 if (chunkType == IDAT_TYPE) {
670
671 stream.skipBytes(-8);
672 imageStartPosition = stream.getStreamPosition();
673 break;
674 } else {
675
676 stream.skipBytes(chunkLength + 4);
677 }
678 }
679 } catch (IOException e) {
680 throw new IIOException("Error skipping PNG metadata", e);
681 }
682
683 gotMetadata = true;
684 return;
685 }
686
687 try {
688 loop: while (true) {
689 int chunkLength = stream.readInt();
690 int chunkType = stream.readInt();
691
692 switch (chunkType) {
693 case IDAT_TYPE:
694
695 stream.skipBytes(-8);
696 imageStartPosition = stream.getStreamPosition();
697 break loop;
698 case PLTE_TYPE:
699 parse_PLTE_chunk(chunkLength);
700 break;
701 case bKGD_TYPE:
702 parse_bKGD_chunk();
703 break;
704 case cHRM_TYPE:
705 parse_cHRM_chunk();
706 break;
707 case gAMA_TYPE:
708 parse_gAMA_chunk();
709 break;
710 case hIST_TYPE:
711 parse_hIST_chunk(chunkLength);
712 break;
713 case iCCP_TYPE:
714 parse_iCCP_chunk(chunkLength);
715 break;
716 case iTXt_TYPE:
717 parse_iTXt_chunk(chunkLength);
718 break;
719 case pHYs_TYPE:
720 parse_pHYs_chunk();
721 break;
722 case sBIT_TYPE:
723 parse_sBIT_chunk();
724 break;
725 case sPLT_TYPE:
726 parse_sPLT_chunk(chunkLength);
727 break;
728 case sRGB_TYPE:
729 parse_sRGB_chunk();
730 break;
731 case tEXt_TYPE:
732 parse_tEXt_chunk(chunkLength);
733 break;
734 case tIME_TYPE:
735 parse_tIME_chunk();
736 break;
737 case tRNS_TYPE:
738 parse_tRNS_chunk(chunkLength);
739 break;
740 case zTXt_TYPE:
741 parse_zTXt_chunk(chunkLength);
742 break;
743 default:
744
745 byte[] b = new byte[chunkLength];
746 stream.readFully(b);
747
748 StringBuilder chunkName = new StringBuilder(4);
749 chunkName.append((char)(chunkType >>> 24));
750 chunkName.append((char)((chunkType >> 16) & 0xff));
751 chunkName.append((char)((chunkType >> 8) & 0xff));
752 chunkName.append((char)(chunkType & 0xff));
753
754 int ancillaryBit = chunkType >>> 28;
755 if (ancillaryBit == 0) {
756 processWarningOccurred(
757 "Encountered unknown chunk with critical bit set!");
758 }
759
760 metadata.unknownChunkType.add(chunkName.toString());
761 metadata.unknownChunkData.add(b);
762 break;
763 }
764
765 int chunkCRC = stream.readInt();
766 stream.flushBefore(stream.getStreamPosition());
767 }
768 } catch (IOException e) {
769 throw new IIOException("Error reading PNG metadata", e);
770 }
771
772 gotMetadata = true;
773 }
774
775
776
777 private static void decodeSubFilter(byte[] curr, int coff, int count,
778 int bpp) {
779 for (int i = bpp; i < count; i++) {
780 int val;
781
782 val = curr[i + coff] & 0xff;
783 val += curr[i + coff - bpp] & 0xff;
784
785 curr[i + coff] = (byte)val;
786 }
787 }
788
789 private static void decodeUpFilter(byte[] curr, int coff,
790 byte[] prev, int poff,
791 int count) {
792 for (int i = 0; i < count; i++) {
793 int raw = curr[i + coff] & 0xff;
794 int prior = prev[i + poff] & 0xff;
795
796 curr[i + coff] = (byte)(raw + prior);
797 }
798 }
799
800 private static void decodeAverageFilter(byte[] curr, int coff,
801 byte[] prev, int poff,
802 int count, int bpp) {
803 int raw, priorPixel, priorRow;
804
805 for (int i = 0; i < bpp; i++) {
806 raw = curr[i + coff] & 0xff;
807 priorRow = prev[i + poff] & 0xff;
808
809 curr[i + coff] = (byte)(raw + priorRow/2);
810 }
811
812 for (int i = bpp; i < count; i++) {
813 raw = curr[i + coff] & 0xff;
814 priorPixel = curr[i + coff - bpp] & 0xff;
815 priorRow = prev[i + poff] & 0xff;
816
817 curr[i + coff] = (byte)(raw + (priorPixel + priorRow)/2);
818 }
819 }
820
821 private static int paethPredictor(int a, int b, int c) {
822 int p = a + b - c;
823 int pa = Math.abs(p - a);
824 int pb = Math.abs(p - b);
825 int pc = Math.abs(p - c);
826
827 if ((pa <= pb) && (pa <= pc)) {
828 return a;
829 } else if (pb <= pc) {
830 return b;
831 } else {
832 return c;
833 }
834 }
835
836 private static void decodePaethFilter(byte[] curr, int coff,
837 byte[] prev, int poff,
838 int count, int bpp) {
839 int raw, priorPixel, priorRow, priorRowPixel;
840
841 for (int i = 0; i < bpp; i++) {
842 raw = curr[i + coff] & 0xff;
843 priorRow = prev[i + poff] & 0xff;
844
845 curr[i + coff] = (byte)(raw + priorRow);
846 }
847
848 for (int i = bpp; i < count; i++) {
849 raw = curr[i + coff] & 0xff;
850 priorPixel = curr[i + coff - bpp] & 0xff;
851 priorRow = prev[i + poff] & 0xff;
852 priorRowPixel = prev[i + poff - bpp] & 0xff;
853
854 curr[i + coff] = (byte)(raw + paethPredictor(priorPixel,
855 priorRow,
856 priorRowPixel));
857 }
858 }
859
860 private static final int[][] bandOffsets = {
861 null,
862 { 0 },
863 { 0, 1 },
864 { 0, 1, 2 },
865 { 0, 1, 2, 3 }
866 };
867
868 private WritableRaster createRaster(int width, int height, int bands,
869 int scanlineStride,
870 int bitDepth) {
871
872 DataBuffer dataBuffer;
873 WritableRaster ras = null;
874 Point origin = new Point(0, 0);
875 if ((bitDepth < 8) && (bands == 1)) {
876 dataBuffer = new DataBufferByte(height*scanlineStride);
877 ras = Raster.createPackedRaster(dataBuffer,
878 width, height,
879 bitDepth,
880 origin);
881 } else if (bitDepth <= 8) {
882 dataBuffer = new DataBufferByte(height*scanlineStride);
883 ras = Raster.createInterleavedRaster(dataBuffer,
884 width, height,
885 scanlineStride,
886 bands,
887 bandOffsets[bands],
888 origin);
889 } else {
890 dataBuffer = new DataBufferUShort(height*scanlineStride);
891 ras = Raster.createInterleavedRaster(dataBuffer,
892 width, height,
893 scanlineStride,
894 bands,
895 bandOffsets[bands],
896 origin);
897 }
898
899 return ras;
900 }
901
902 private void skipPass(int passWidth, int passHeight)
903 throws IOException, IIOException {
904 if ((passWidth == 0) || (passHeight == 0)) {
905 return;
906 }
907
908 int inputBands = inputBandsForColorType[metadata.IHDR_colorType];
909 int bytesPerRow = (inputBands*passWidth*metadata.IHDR_bitDepth + 7)/8;
910
911
912 for (int srcY = 0; srcY < passHeight; srcY++) {
913
914 pixelStream.skipBytes(1 + bytesPerRow);
915
916
917
918 if (abortRequested()) {
919 return;
920 }
921 }
922 }
923
924 private void updateImageProgress(int newPixels) {
925 pixelsDone += newPixels;
926 processImageProgress(100.0F*pixelsDone/totalPixels);
927 }
928
929 private void decodePass(int passNum,
930 int xStart, int yStart,
931 int xStep, int yStep,
932 int passWidth, int passHeight) throws IOException {
933
934 if ((passWidth == 0) || (passHeight == 0)) {
935 return;
936 }
937
938 WritableRaster imRas = theImage.getWritableTile(0, 0);
939 int dstMinX = imRas.getMinX();
940 int dstMaxX = dstMinX + imRas.getWidth() - 1;
941 int dstMinY = imRas.getMinY();
942 int dstMaxY = dstMinY + imRas.getHeight() - 1;
943
944
945 int[] vals =
946 ReaderUtil.computeUpdatedPixels(sourceRegion,
947 destinationOffset,
948 dstMinX, dstMinY,
949 dstMaxX, dstMaxY,
950 sourceXSubsampling,
951 sourceYSubsampling,
952 xStart, yStart,
953 passWidth, passHeight,
954 xStep, yStep);
955 int updateMinX = vals[0];
956 int updateMinY = vals[1];
957 int updateWidth = vals[2];
958 int updateXStep = vals[4];
959 int updateYStep = vals[5];
960
961 int bitDepth = metadata.IHDR_bitDepth;
962 int inputBands = inputBandsForColorType[metadata.IHDR_colorType];
963 int bytesPerPixel = (bitDepth == 16) ? 2 : 1;
964 bytesPerPixel *= inputBands;
965
966 int bytesPerRow = (inputBands*passWidth*bitDepth + 7)/8;
967 int eltsPerRow = (bitDepth == 16) ? bytesPerRow/2 : bytesPerRow;
968
969
970 if (updateWidth == 0) {
971 for (int srcY = 0; srcY < passHeight; srcY++) {
972
973 updateImageProgress(passWidth);
974
975 pixelStream.skipBytes(1 + bytesPerRow);
976 }
977 return;
978 }
979
980
981
982
983
984 int sourceX =
985 (updateMinX - destinationOffset.x)*sourceXSubsampling +
986 sourceRegion.x;
987 int srcX = (sourceX - xStart)/xStep;
988
989
990 int srcXStep = updateXStep*sourceXSubsampling/xStep;
991
992 byte[] byteData = null;
993 short[] shortData = null;
994 byte[] curr = new byte[bytesPerRow];
995 byte[] prior = new byte[bytesPerRow];
996
997
998 WritableRaster passRow = createRaster(passWidth, 1, inputBands,
999 eltsPerRow,
1000 bitDepth);
1001
1002
1003 int[] ps = passRow.getPixel(0, 0, (int[])null);
1004
1005 DataBuffer dataBuffer = passRow.getDataBuffer();
1006 int type = dataBuffer.getDataType();
1007 if (type == DataBuffer.TYPE_BYTE) {
1008 byteData = ((DataBufferByte)dataBuffer).getData();
1009 } else {
1010 shortData = ((DataBufferUShort)dataBuffer).getData();
1011 }
1012
1013 processPassStarted(theImage,
1014 passNum,
1015 sourceMinProgressivePass,
1016 sourceMaxProgressivePass,
1017 updateMinX, updateMinY,
1018 updateXStep, updateYStep,
1019 destinationBands);
1020
1021
1022 if (sourceBands != null) {
1023 passRow = passRow.createWritableChild(0, 0,
1024 passRow.getWidth(), 1,
1025 0, 0,
1026 sourceBands);
1027 }
1028 if (destinationBands != null) {
1029 imRas = imRas.createWritableChild(0, 0,
1030 imRas.getWidth(),
1031 imRas.getHeight(),
1032 0, 0,
1033 destinationBands);
1034 }
1035
1036
1037
1038 boolean adjustBitDepths = false;
1039 int[] outputSampleSize = imRas.getSampleModel().getSampleSize();
1040 int numBands = outputSampleSize.length;
1041 for (int b = 0; b < numBands; b++) {
1042 if (outputSampleSize[b] != bitDepth) {
1043 adjustBitDepths = true;
1044 break;
1045 }
1046 }
1047
1048
1049
1050 int[][] scale = null;
1051 if (adjustBitDepths) {
1052 int maxInSample = (1 << bitDepth) - 1;
1053 int halfMaxInSample = maxInSample/2;
1054 scale = new int[numBands][];
1055 for (int b = 0; b < numBands; b++) {
1056 int maxOutSample = (1 << outputSampleSize[b]) - 1;
1057 scale[b] = new int[maxInSample + 1];
1058 for (int s = 0; s <= maxInSample; s++) {
1059 scale[b][s] =
1060 (s*maxOutSample + halfMaxInSample)/maxInSample;
1061 }
1062 }
1063 }
1064
1065
1066
1067 boolean useSetRect = srcXStep == 1 &&
1068 updateXStep == 1 &&
1069 !adjustBitDepths &&
1070 (imRas instanceof ByteInterleavedRaster);
1071
1072 if (useSetRect) {
1073 passRow = passRow.createWritableChild(srcX, 0,
1074 updateWidth, 1,
1075 0, 0,
1076 null);
1077 }
1078
1079
1080 for (int srcY = 0; srcY < passHeight; srcY++) {
1081
1082 updateImageProgress(passWidth);
1083
1084
1085 int filter = pixelStream.read();
1086 try {
1087
1088 byte[] tmp = prior;
1089 prior = curr;
1090 curr = tmp;
1091
1092 pixelStream.readFully(curr, 0, bytesPerRow);
1093 } catch (java.util.zip.ZipException ze) {
1094
1095 throw ze;
1096 }
1097
1098 switch (filter) {
1099 case PNG_FILTER_NONE:
1100 break;
1101 case PNG_FILTER_SUB:
1102 decodeSubFilter(curr, 0, bytesPerRow, bytesPerPixel);
1103 break;
1104 case PNG_FILTER_UP:
1105 decodeUpFilter(curr, 0, prior, 0, bytesPerRow);
1106 break;
1107 case PNG_FILTER_AVERAGE:
1108 decodeAverageFilter(curr, 0, prior, 0, bytesPerRow,
1109 bytesPerPixel);
1110 break;
1111 case PNG_FILTER_PAETH:
1112 decodePaethFilter(curr, 0, prior, 0, bytesPerRow,
1113 bytesPerPixel);
1114 break;
1115 default:
1116 throw new IIOException("Unknown row filter type (= " +
1117 filter + ")!");
1118 }
1119
1120
1121 if (bitDepth < 16) {
1122 System.arraycopy(curr, 0, byteData, 0, bytesPerRow);
1123 } else {
1124 int idx = 0;
1125 for (int j = 0; j < eltsPerRow; j++) {
1126 shortData[j] =
1127 (short)((curr[idx] << 8) | (curr[idx + 1] & 0xff));
1128 idx += 2;
1129 }
1130 }
1131
1132
1133 int sourceY = srcY*yStep + yStart;
1134 if ((sourceY >= sourceRegion.y) &&
1135 (sourceY < sourceRegion.y + sourceRegion.height) &&
1136 (((sourceY - sourceRegion.y) %
1137 sourceYSubsampling) == 0)) {
1138
1139 int dstY = destinationOffset.y +
1140 (sourceY - sourceRegion.y)/sourceYSubsampling;
1141 if (dstY < dstMinY) {
1142 continue;
1143 }
1144 if (dstY > dstMaxY) {
1145 break;
1146 }
1147
1148 if (useSetRect) {
1149 imRas.setRect(updateMinX, dstY, passRow);
1150 } else {
1151 int newSrcX = srcX;
1152
1153 for (int dstX = updateMinX;
1154 dstX < updateMinX + updateWidth;
1155 dstX += updateXStep) {
1156
1157 passRow.getPixel(newSrcX, 0, ps);
1158 if (adjustBitDepths) {
1159 for (int b = 0; b < numBands; b++) {
1160 ps[b] = scale[b][ps[b]];
1161 }
1162 }
1163 imRas.setPixel(dstX, dstY, ps);
1164 newSrcX += srcXStep;
1165 }
1166 }
1167
1168 processImageUpdate(theImage,
1169 updateMinX, dstY,
1170 updateWidth, 1,
1171 updateXStep, updateYStep,
1172 destinationBands);
1173
1174
1175
1176 if (abortRequested()) {
1177 return;
1178 }
1179 }
1180 }
1181
1182 processPassComplete(theImage);
1183 }
1184
1185 private void decodeImage()
1186 throws IOException, IIOException {
1187 int width = metadata.IHDR_width;
1188 int height = metadata.IHDR_height;
1189
1190 this.pixelsDone = 0;
1191 this.totalPixels = width*height;
1192
1193 clearAbortRequest();
1194
1195 if (metadata.IHDR_interlaceMethod == 0) {
1196 decodePass(0, 0, 0, 1, 1, width, height);
1197 } else {
1198 for (int i = 0; i <= sourceMaxProgressivePass; i++) {
1199 int XOffset = adam7XOffset[i];
1200 int YOffset = adam7YOffset[i];
1201 int XSubsampling = adam7XSubsampling[i];
1202 int YSubsampling = adam7YSubsampling[i];
1203 int xbump = adam7XSubsampling[i + 1] - 1;
1204 int ybump = adam7YSubsampling[i + 1] - 1;
1205
1206 if (i >= sourceMinProgressivePass) {
1207 decodePass(i,
1208 XOffset,
1209 YOffset,
1210 XSubsampling,
1211 YSubsampling,
1212 (width + xbump)/XSubsampling,
1213 (height + ybump)/YSubsampling);
1214 } else {
1215 skipPass((width + xbump)/XSubsampling,
1216 (height + ybump)/YSubsampling);
1217 }
1218
1219
1220
1221 if (abortRequested()) {
1222 return;
1223 }
1224 }
1225 }
1226 }
1227
1228 private void readImage(ImageReadParam param) throws IIOException {
1229 readMetadata();
1230
1231 int width = metadata.IHDR_width;
1232 int height = metadata.IHDR_height;
1233
1234
1235 sourceXSubsampling = 1;
1236 sourceYSubsampling = 1;
1237 sourceMinProgressivePass = 0;
1238 sourceMaxProgressivePass = 6;
1239 sourceBands = null;
1240 destinationBands = null;
1241 destinationOffset = new Point(0, 0);
1242
1243
1244 if (param != null) {
1245 sourceXSubsampling = param.getSourceXSubsampling();
1246 sourceYSubsampling = param.getSourceYSubsampling();
1247
1248 sourceMinProgressivePass =
1249 Math.max(param.getSourceMinProgressivePass(), 0);
1250 sourceMaxProgressivePass =
1251 Math.min(param.getSourceMaxProgressivePass(), 6);
1252
1253 sourceBands = param.getSourceBands();
1254 destinationBands = param.getDestinationBands();
1255 destinationOffset = param.getDestinationOffset();
1256 }
1257 Inflater inf = null;
1258 try {
1259 stream.seek(imageStartPosition);
1260
1261 Enumeration<InputStream> e = new PNGImageDataEnumeration(stream);
1262 InputStream is = new SequenceInputStream(e);
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275 inf = new Inflater();
1276 is = new InflaterInputStream(is, inf);
1277 is = new BufferedInputStream(is);
1278 this.pixelStream = new DataInputStream(is);
1279
1280 theImage = getDestination(param,
1281 getImageTypes(0),
1282 width,
1283 height);
1284
1285 Rectangle destRegion = new Rectangle(0, 0, 0, 0);
1286 sourceRegion = new Rectangle(0, 0, 0, 0);
1287 computeRegions(param, width, height,
1288 theImage,
1289 sourceRegion, destRegion);
1290 destinationOffset.setLocation(destRegion.getLocation());
1291
1292
1293
1294
1295 int colorType = metadata.IHDR_colorType;
1296 checkReadParamBandSettings(param,
1297 inputBandsForColorType[colorType],
1298 theImage.getSampleModel().getNumBands());
1299
1300 processImageStarted(0);
1301 decodeImage();
1302 if (abortRequested()) {
1303 processReadAborted();
1304 } else {
1305 processImageComplete();
1306 }
1307 } catch (IOException e) {
1308 throw new IIOException("Error reading PNG image data", e);
1309 } finally {
1310 if (inf != null) {
1311 inf.end();
1312 }
1313 }
1314 }
1315
1316 public int getNumImages(boolean allowSearch) throws IIOException {
1317 if (stream == null) {
1318 throw new IllegalStateException("No input source set!");
1319 }
1320 if (seekForwardOnly && allowSearch) {
1321 throw new IllegalStateException
1322 ("seekForwardOnly and allowSearch can't both be true!");
1323 }
1324 return 1;
1325 }
1326
1327 public int getWidth(int imageIndex) throws IIOException {
1328 if (imageIndex != 0) {
1329 throw new IndexOutOfBoundsException("imageIndex != 0!");
1330 }
1331
1332 readHeader();
1333
1334 return metadata.IHDR_width;
1335 }
1336
1337 public int getHeight(int imageIndex) throws IIOException {
1338 if (imageIndex != 0) {
1339 throw new IndexOutOfBoundsException("imageIndex != 0!");
1340 }
1341
1342 readHeader();
1343
1344 return metadata.IHDR_height;
1345 }
1346
1347 public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex)
1348 throws IIOException
1349 {
1350 if (imageIndex != 0) {
1351 throw new IndexOutOfBoundsException("imageIndex != 0!");
1352 }
1353
1354 readHeader();
1355
1356 ArrayList<ImageTypeSpecifier> l =
1357 new ArrayList<ImageTypeSpecifier>(1);
1358
1359 ColorSpace rgb;
1360 ColorSpace gray;
1361 int[] bandOffsets;
1362
1363 int bitDepth = metadata.IHDR_bitDepth;
1364 int colorType = metadata.IHDR_colorType;
1365
1366 int dataType;
1367 if (bitDepth <= 8) {
1368 dataType = DataBuffer.TYPE_BYTE;
1369 } else {
1370 dataType = DataBuffer.TYPE_USHORT;
1371 }
1372
1373 switch (colorType) {
1374 case PNG_COLOR_GRAY:
1375
1376 l.add(ImageTypeSpecifier.createGrayscale(bitDepth,
1377 dataType,
1378 false));
1379 break;
1380
1381 case PNG_COLOR_RGB:
1382 if (bitDepth == 8) {
1383
1384
1385 l.add(ImageTypeSpecifier.createFromBufferedImageType(
1386 BufferedImage.TYPE_3BYTE_BGR));
1387
1388 l.add(ImageTypeSpecifier.createFromBufferedImageType(
1389 BufferedImage.TYPE_INT_RGB));
1390
1391 l.add(ImageTypeSpecifier.createFromBufferedImageType(
1392 BufferedImage.TYPE_INT_BGR));
1393
1394 }
1395
1396 rgb = ColorSpace.getInstance(ColorSpace.CS_sRGB);
1397 bandOffsets = new int[3];
1398 bandOffsets[0] = 0;
1399 bandOffsets[1] = 1;
1400 bandOffsets[2] = 2;
1401 l.add(ImageTypeSpecifier.createInterleaved(rgb,
1402 bandOffsets,
1403 dataType,
1404 false,
1405 false));
1406 break;
1407
1408 case PNG_COLOR_PALETTE:
1409 readMetadata();
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435 int plength = 1 << bitDepth;
1436
1437 byte[] red = metadata.PLTE_red;
1438 byte[] green = metadata.PLTE_green;
1439 byte[] blue = metadata.PLTE_blue;
1440
1441 if (metadata.PLTE_red.length < plength) {
1442 red = Arrays.copyOf(metadata.PLTE_red, plength);
1443 Arrays.fill(red, metadata.PLTE_red.length, plength,
1444 metadata.PLTE_red[metadata.PLTE_red.length - 1]);
1445
1446 green = Arrays.copyOf(metadata.PLTE_green, plength);
1447 Arrays.fill(green, metadata.PLTE_green.length, plength,
1448 metadata.PLTE_green[metadata.PLTE_green.length - 1]);
1449
1450 blue = Arrays.copyOf(metadata.PLTE_blue, plength);
1451 Arrays.fill(blue, metadata.PLTE_blue.length, plength,
1452 metadata.PLTE_blue[metadata.PLTE_blue.length - 1]);
1453
1454 }
1455
1456
1457
1458
1459 byte[] alpha = null;
1460 if (metadata.tRNS_present && (metadata.tRNS_alpha != null)) {
1461 if (metadata.tRNS_alpha.length == red.length) {
1462 alpha = metadata.tRNS_alpha;
1463 } else {
1464 alpha = Arrays.copyOf(metadata.tRNS_alpha, red.length);
1465 Arrays.fill(alpha,
1466 metadata.tRNS_alpha.length,
1467 red.length, (byte)255);
1468 }
1469 }
1470
1471 l.add(ImageTypeSpecifier.createIndexed(red, green,
1472 blue, alpha,
1473 bitDepth,
1474 DataBuffer.TYPE_BYTE));
1475 break;
1476
1477 case PNG_COLOR_GRAY_ALPHA:
1478
1479 gray = ColorSpace.getInstance(ColorSpace.CS_GRAY);
1480 bandOffsets = new int[2];
1481 bandOffsets[0] = 0;
1482 bandOffsets[1] = 1;
1483 l.add(ImageTypeSpecifier.createInterleaved(gray,
1484 bandOffsets,
1485 dataType,
1486 true,
1487 false));
1488 break;
1489
1490 case PNG_COLOR_RGB_ALPHA:
1491 if (bitDepth == 8) {
1492
1493
1494 l.add(ImageTypeSpecifier.createFromBufferedImageType(
1495 BufferedImage.TYPE_4BYTE_ABGR));
1496
1497 l.add(ImageTypeSpecifier.createFromBufferedImageType(
1498 BufferedImage.TYPE_INT_ARGB));
1499 }
1500
1501
1502 rgb = ColorSpace.getInstance(ColorSpace.CS_sRGB);
1503 bandOffsets = new int[4];
1504 bandOffsets[0] = 0;
1505 bandOffsets[1] = 1;
1506 bandOffsets[2] = 2;
1507 bandOffsets[3] = 3;
1508
1509 l.add(ImageTypeSpecifier.createInterleaved(rgb,
1510 bandOffsets,
1511 dataType,
1512 true,
1513 false));
1514 break;
1515
1516 default:
1517 break;
1518 }
1519
1520 return l.iterator();
1521 }
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543 public ImageTypeSpecifier getRawImageType(int imageIndex)
1544 throws IOException {
1545
1546 Iterator<ImageTypeSpecifier> types = getImageTypes(imageIndex);
1547 ImageTypeSpecifier raw = null;
1548 do {
1549 raw = types.next();
1550 } while (types.hasNext());
1551 return raw;
1552 }
1553
1554 public ImageReadParam getDefaultReadParam() {
1555 return new ImageReadParam();
1556 }
1557
1558 public IIOMetadata getStreamMetadata()
1559 throws IIOException {
1560 return null;
1561 }
1562
1563 public IIOMetadata getImageMetadata(int imageIndex) throws IIOException {
1564 if (imageIndex != 0) {
1565 throw new IndexOutOfBoundsException("imageIndex != 0!");
1566 }
1567 readMetadata();
1568 return metadata;
1569 }
1570
1571 public BufferedImage read(int imageIndex, ImageReadParam param)
1572 throws IIOException {
1573 if (imageIndex != 0) {
1574 throw new IndexOutOfBoundsException("imageIndex != 0!");
1575 }
1576
1577 readImage(param);
1578 return theImage;
1579 }
1580
1581 public void reset() {
1582 super.reset();
1583 resetStreamSettings();
1584 }
1585
1586 private void resetStreamSettings() {
1587 gotHeader = false;
1588 gotMetadata = false;
1589 metadata = null;
1590 pixelStream = null;
1591 }
1592 }